package com.jintian.smart.kernel.orm.raw.condition;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.jintian.smart.kernel.common.util.JsonUtil;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.exception.SmartException;

import java.util.*;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = LogicCondition.class, name = "LogicQuery"),
        @JsonSubTypes.Type(value = ArithCondition.class, name = "ArithQuery")
})
public class RawCondition {
    private List<RawCondition> queries = new ArrayList<>();

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    public List<RawCondition> getQueries() {
        return queries;
    }

    // 多条件构建sql
    public String toSql() {
        String sql = where();
        if (StringUtil.isEmpty(sql)) {
            return sql;
        } else {
            return "(" + sql + ")";
        }
    }

    public String where() {
        if (queries.size() > 0) {
            StringBuilder sb = new StringBuilder();
            for (RawCondition expression : queries) {
                sb.append(expression.toSql());
            }

            String sql = sb.toString();
            sql = sql.replaceAll("^( or  | and  )", "");
            return sql;
        }
        return "";
    }

    // 多条件构建参数列表

    public List<Object> toParamList() {
        if (queries.size() > 0) {
            List<Object> params = new ArrayList<>();
            for (RawCondition expression : queries) {
                params.addAll(expression.toParamList());
            }
            return params;
        }
        return Collections.emptyList();
    }

    public Object[] toParams() {
        if (queries.size() > 0) {
            List<Object> params = new ArrayList<>();
            for (RawCondition expression : queries) {
                params.addAll(expression.toParamList());
            }
            return params.toArray(new Object[0]);
        }
        return new Object[0];
    }

    public RawCondition and(RawCondition query) {
        queries.add(new LogicCondition(Logic.AND, query));
        return this;
    }

    public RawCondition or(RawCondition query) {
        queries.add(new LogicCondition(Logic.OR, query));
        return this;
    }

    public RawCondition andEq(String field, Object value) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.Eq, field, value)));
        return this;
    }

    public RawCondition andNotEq(String field, Object value) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.NotEq, field, value)));
        return this;
    }

    public RawCondition andGreat(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.Great, field, value)));
        return this;
    }

    public RawCondition andGreatEq(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.GreatEq, field, value)));
        return this;
    }

    public RawCondition andLess(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.Less, field, value)));
        return this;
    }

    public RawCondition andLessEq(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.LessEq, field, value)));
        return this;
    }

    public RawCondition andLike(String field, Object value) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.Like, field, value)));
        return this;
    }

    public RawCondition andNotLike(String field, Object value) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.NotLike, field, value)));
        return this;
    }

    public RawCondition andIsNull(String field) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.IsNull, field)));
        return this;
    }

    public RawCondition andIsNotNull(String field) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.IsNotNull, field)));
        return this;
    }

    public RawCondition andIn(String field, List<Object> value) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.In, field, value)));
        return this;
    }

    public RawCondition andNotIn(String field, Object value) {
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.NotIn, field, value)));
        return this;
    }

    public RawCondition andBetween(String field, Object value, Object value2) {
        assertCompare(value);
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.Between, field, value, value2)));
        return this;
    }

    public RawCondition andNotBetween(String field, Object value, Object value2) {
        assertCompare(value);
        queries.add(new LogicCondition(Logic.AND, new ArithCondition(Arith.NotBetween, field, value, value2)));
        return this;
    }


    public RawCondition orEq(String field, Object value) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.Eq, field, value)));
        return this;
    }

    public RawCondition orNotEq(String field, Object value) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.NotEq, field, value)));
        return this;
    }

    public RawCondition orGreat(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.Great, field, value)));
        return this;
    }

    public RawCondition orGreatEq(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.GreatEq, field, value)));
        return this;
    }

    public RawCondition orLess(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.Less, field, value)));
        return this;
    }

    public RawCondition orLessEq(String field, Object value) {
        assertNumber(value);
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.LessEq, field, value)));
        return this;
    }

    public RawCondition orLike(String field, String value) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.Like, field, value)));
        return this;
    }

    public RawCondition orNotLike(String field, String value) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.NotLike, field, value)));
        return this;
    }

    public RawCondition orIsNull(String field) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.IsNull, field)));
        return this;
    }

    public RawCondition orIsNotNull(String field) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.IsNotNull, field)));
        return this;
    }

    public RawCondition orIn(String field, Object value) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.In, field, value)));
        return this;
    }

    public RawCondition orNotIn(String field, Object value) {
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.NotIn, field, value)));
        return this;
    }

    public RawCondition orBetween(String field, Object value, Object value2) {
        assertCompare(value);
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.Between, field, value, value2)));
        return this;
    }

    public RawCondition orNotBetween(String field, Object value, Object value2) {
        assertCompare(value);
        queries.add(new LogicCondition(Logic.OR, new ArithCondition(Arith.NotBetween, field, value, value2)));
        return this;
    }

    private void assertNumber(Object value) {
        if (!(value instanceof Number)) {
            throw new SmartException(String.format("参数%s不是数字类型", value.toString()));
        }
    }

    private void assertCompare(Object value) {
        if (!(value instanceof Number || value instanceof Date)) {
            throw new SmartException(String.format("参数%s不是数字类型，也不是时间类型", value.toString()));
        }
    }

    public static void main(String[] args) {
        String json2 = JsonUtil.toJson(Logic.AND);
        System.out.println(json2);

        RawCondition sub = new RawCondition();
        sub.orEq("c", 2);
        sub.orEq("d", "d");

        RawCondition query = new RawCondition();
        query.andEq("a", 1);
        query.andEq("b", "b");
        query.andIn("aa", Arrays.asList(11, 22, 33));
        query.andIn("bb", Arrays.asList("bb1", "bb2"));
        query.andBetween("aaa", 111, 222);
        query.andIsNull("bbb");
        query.and(sub);

        System.out.println(query.where());
        List<Object> params = query.toParamList();
        String paramsString = String.join(", ", params.stream().map(Object::toString).toArray(String[]::new));
        System.out.println("Parameters: " + paramsString);

        String json = JsonUtil.toJson(query);
        System.out.println(json);
        RawCondition query2 = JsonUtil.readValue(json, RawCondition.class);
        System.out.println(JsonUtil.toJson(query2));
    }
}
